package com.struts2.gae.dispatcher.multipart;

import java.io.IOException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Map;
import java.util.Vector;

import javax.servlet.http.HttpServletRequest;

import org.apache.struts2.dispatcher.StrutsRequestWrapper;

import com.opensymphony.xwork2.util.logging.Logger;
import com.opensymphony.xwork2.util.logging.LoggerFactory;

/**
 * Parse a multipart request and provide a wrapper around the request. The files
 * are uploaded when the object is instantiated. If there are any errors they
 * are logged using {@link #addError(String)}. An action handling a multipart
 * form should first check {@link #hasErrors()} before doing any other
 * processing.
 * 
 * @author whyjava7@gmail.com
 * @version 0.1
 * 
 */
public class GaeMultiPartRequestWrapper extends StrutsRequestWrapper {

	protected static final Logger LOG = LoggerFactory
			.getLogger(GaeMultiPartRequestWrapper.class);

	Collection<String> errors;
	GaeMultiPartRequest multi;

	public GaeMultiPartRequestWrapper(GaeMultiPartRequest multiPartRequest,
			HttpServletRequest request) {
		super(request);
		multi = multiPartRequest;
		try {
			multi.parse(request);
			for (Object o : multi.getErrors()) {
				String error = (String) o;
				addError(error);
			}
		} catch (IOException e) {
			addError("Cannot parse request: " + e.toString());
		}

	}

	/**
	 * Adds an error message.
	 * 
	 * @param anErrorMessage
	 *            the error message to report.
	 */
	protected void addError(String anErrorMessage) {
		if (errors == null) {
			errors = new ArrayList<String>();
		}

		errors.add(anErrorMessage);
	}

	/**
	 * Returns <tt>true</tt> if any errors occured when parsing the HTTP
	 * multipart request, <tt>false</tt> otherwise.
	 * 
	 * @return <tt>true</tt> if any errors occured when parsing the HTTP
	 *         multipart request, <tt>false</tt> otherwise.
	 */
	public boolean hasErrors() {
		return !((errors == null) || errors.isEmpty());
	}

	/**
	 * Returns a collection of any errors generated when parsing the multipart
	 * request.
	 * 
	 * @return the error Collection.
	 */
	public Collection<String> getErrors() {
		return errors;
	}

	/**
	 * Get an enumeration of the parameter names for uploaded files
	 * 
	 * @return enumeration of parameter names for uploaded files
	 */
	public Enumeration<String> getFileParameterNames() {
		if (multi == null) {
			return null;
		}

		return multi.getFileParameterNames();
	}

	/**
	 * Get an array of content encoding for the specified input field name or
	 * <tt>null</tt> if no content type was specified.
	 * 
	 * @param name
	 *            input field name
	 * @return an array of content encoding for the specified input field name
	 */
	public String[] getContentTypes(String name) {
		if (multi == null) {
			return null;
		}

		return multi.getContentType(name);
	}

	public String[] getFileContents(String fieldName) {
		if (multi == null) {
			return null;
		}

		return multi.getFileContents(fieldName);
	}

	/**
	 * Get a String array of the file names for uploaded files
	 * 
	 * @param fieldName
	 *            Field to check for file names.
	 * @return a String[] of file names for uploaded files
	 */
	public String[] getFileNames(String fieldName) {
		if (multi == null) {
			return null;
		}

		return multi.getFileNames(fieldName);
	}

	/**
	 * @see javax.servlet.http.HttpServletRequest#getParameter(String)
	 */
	public String getParameter(String name) {
		return ((multi == null) || (multi.getParameter(name) == null)) ? super
				.getParameter(name) : multi.getParameter(name);
	}

	/**
	 * @see javax.servlet.http.HttpServletRequest#getParameterMap()
	 */
	public Map getParameterMap() {
		Map<String, String[]> map = new HashMap<String, String[]>();
		Enumeration enumeration = getParameterNames();

		while (enumeration.hasMoreElements()) {
			String name = (String) enumeration.nextElement();
			map.put(name, this.getParameterValues(name));
		}

		return map;
	}

	/**
	 * @see javax.servlet.http.HttpServletRequest#getParameterNames()
	 */
	public Enumeration getParameterNames() {
		if (multi == null) {
			return super.getParameterNames();
		} else {
			return mergeParams(multi.getParameterNames(),
					super.getParameterNames());
		}
	}

	/**
	 * @see javax.servlet.http.HttpServletRequest#getParameterValues(String)
	 */
	public String[] getParameterValues(String name) {
		return ((multi == null) || (multi.getParameterValues(name) == null)) ? super
				.getParameterValues(name) : multi.getParameterValues(name);
	}

	/**
	 * Merges 2 enumeration of parameters as one.
	 * 
	 * @param params1
	 *            the first enumeration.
	 * @param params2
	 *            the second enumeration.
	 * @return a single Enumeration of all elements from both Enumerations.
	 */
	protected Enumeration mergeParams(Enumeration params1, Enumeration params2) {
		Vector temp = new Vector();

		while (params1.hasMoreElements()) {
			temp.add(params1.nextElement());
		}

		while (params2.hasMoreElements()) {
			temp.add(params2.nextElement());
		}

		return temp.elements();
	}
}
